// Package testing provides mock components for testing
package testing

import (
	"sync"
	"testing"
	"time"
)

// MockComponent provides a base for mock components
type MockComponent struct {
	Calls      []MockCall
	ReturnValues map[string]interface{}
	Errors     map[string]error
	mu         sync.Mutex
}

// MockCall represents a method call
type MockCall struct {
	MethodName string
	Args       []interface{}
	Timestamp  time.Time
}

// NewMockComponent creates a new mock component
func NewMockComponent() *MockComponent {
	return &MockComponent{
		Calls:        make([]MockCall, 0),
		ReturnValues: make(map[string]interface{}),
		Errors:       make(map[string]error),
	}
}

// RecordCall records a method call
func (m *MockComponent) RecordCall(methodName string, args ...interface{}) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.Calls = append(m.Calls, MockCall{
		MethodName: methodName,
		Args:       args,
		Timestamp:  time.Now(),
	})
}

// SetReturnValue sets a return value for a method
func (m *MockComponent) SetReturnValue(methodName string, value interface{}) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.ReturnValues[methodName] = value
}

// SetError sets an error for a method
func (m *MockComponent) SetError(methodName string, err error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.Errors[methodName] = err
}

// GetReturnValue gets the return value for a method
func (m *MockComponent) GetReturnValue(methodName string) (interface{}, bool) {
	m.mu.Lock()
	defer m.mu.Unlock()

	value, exists := m.ReturnValues[methodName]
	return value, exists
}

// GetError gets the error for a method
func (m *MockComponent) GetError(methodName string) (error, bool) {
	m.mu.Lock()
	defer m.mu.Unlock()

	err, exists := m.Errors[methodName]
	return err, exists
}

// GetCallCount returns the number of calls for a method
func (m *MockComponent) GetCallCount(methodName string) int {
	m.mu.Lock()
	defer m.mu.Unlock()

	count := 0
	for _, call := range m.Calls {
		if call.MethodName == methodName {
			count++
		}
	}
	return count
}

// AssertCalled asserts that a method was called
func (m *MockComponent) AssertCalled(t *testing.T, methodName string) {
	if m.GetCallCount(methodName) == 0 {
		t.Errorf("Method %s was not called", methodName)
	}
}

// AssertNotCalled asserts that a method was not called
func (m *MockComponent) AssertNotCalled(t *testing.T, methodName string) {
	if m.GetCallCount(methodName) > 0 {
		t.Errorf("Method %s was called %d times", methodName, m.GetCallCount(methodName))
	}
}

// AssertCalledWith asserts that a method was called with specific arguments
func (m *MockComponent) AssertCalledWith(t *testing.T, methodName string, expectedArgs ...interface{}) {
	m.mu.Lock()
	defer m.mu.Unlock()

	for _, call := range m.Calls {
		if call.MethodName == methodName {
			if len(call.Args) == len(expectedArgs) {
				match := true
				for i, arg := range call.Args {
					if arg != expectedArgs[i] {
						match = false
						break
					}
				}
				if match {
					return
				}
			}
		}
	}

	t.Errorf("Method %s was not called with expected arguments %v", methodName, expectedArgs)
}